package pers.whj.util.list;

import pers.whj.util.NoSuchElementException;

/**
 * implements interface Queue, use array to store the data, and has fixed length
 *
 * @param <T> the type of data
 */
public class QueueImpl<T> implements Queue<T> {
    protected Object[] mData;
    /**
     * the real number of data can be stored
     */
    protected int mMaxLength;
    protected int mHead;
    protected int mTail;

    public QueueImpl(int maxLength) {
        mMaxLength = Math.max(maxLength, 0);
        mData = new Object[mMaxLength + 1];
        mHead = mTail = 0;
    }

    @Override
    public int length() {
        return (mTail + mData.length - mHead) % (mData.length);
    }

    @Override
    public int maxLength() {
        return mMaxLength;
    }

    @Override
    public boolean offer(T data) {
        checkForAdd();
        mTail = (mTail + 1) % mData.length;
        mData[mTail] = data;
        return true;
    }

    @Override
    public T poll() {
        checkForRemove();
        mHead = (mHead + 1) % mData.length;
        T data = data(mHead);
        mData[mHead] = null;
        return data;
    }

    @Override
    public T peek() {
        checkForRemove();
        return data((mHead + 1) % mData.length);
    }

    @Override
    public boolean isEmpty() {
        return mHead == mTail;
    }

    @Override
    public boolean isFull() {
        return length() == mMaxLength;
    }

    @SuppressWarnings("unchecked")
    protected T data(int index) {
        return (T) mData[index];
    }

    protected void checkForAdd() {
        if (isFull()) {
            throw new IllegalStateException("queue is full, current length: " + mMaxLength);
        }
    }

    protected void checkForRemove() {
        if (isEmpty()) {
            throw new NoSuchElementException("queue is empty");
        }
    }
}